package ferp.android.views;

import android.content.Context;
import android.graphics.*;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.TextView;
import ferp.android.GUI;
import ferp.android.R;
import ferp.core.calc.Calculator;
import ferp.core.game.Game;
import ferp.core.game.Settings;
import ferp.core.player.Player;
import ferp.core.player.Profile;
import ferp.core.player.Score;

import java.util.Iterator;

/**
 * User: olegs
 * Date: 27/06/11 22:25
 */
public class PoolView extends ViewGroup
{
  public PoolView(Context context)
  {
    this(context, null);
  }

  public PoolView(Context context, AttributeSet attrs)
  {
    super(context, attrs);

    setWillNotDraw(false);

    watermark = new TextView(context);

    watermark.setGravity(Gravity.CENTER);
    watermark.setTypeface(GUI.Font.BOLD);
    //watermark.setTextColor(0x55FF0000);
    watermark.setTextColor(GUI.Color.WATERMARK_POOL);
    watermark.setVisibility(GONE);
    watermark.setText(R.string.dialog_pool_replay_noun);

    addView(watermark);

    pool = new TextView(context);

    pool.setGravity(Gravity.CENTER);
    pool.setTypeface(GUI.Font.BOLD);
    pool.setTextColor(Color.BLACK);
    //pool.setPadding(0, 0, 0, 0);
    addView(pool);

    size = Integer.MAX_VALUE;
  }

  public void initialize(Profile profile)
  {
    this.profile = profile;

    Game game = profile.game();
    settings = profile.settings;
    calculator = Calculator.get(settings.gameFlavor);

    leftScore   = new PlayerScore(game.next(game.human()));
    middleScore = new PlayerScore(game.human());
    rightScore  = new PlayerScore(game.previous(game.human()));

    // this is to ensure that animation starts right after layout completed
    this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
    {
      @Override
      public void onGlobalLayout()
      {
        // make sure this happens only once
        GUI.removeGlobalOnLayoutListener(PoolView.this, this);

        // start rotate animation
        leftScore.rotate(90, center.x, center.y);
        rightScore.rotate(-90, center.x, center.y);
      }
    });
  }

  public void showWatermark(boolean replay) {watermark.setVisibility(replay ? VISIBLE : GONE);}

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  {
    size = Math.min(size, Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)));

    setMeasuredDimension(size, size);
  }

  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom)
  {
    if (changed)
    {
      width = Math.min(right - left, bottom - top);
      height = Math.max(right - left, bottom - top);
      center.x = left + width / 2;
      center.y = top + height / 2;

      initializePool();

      int h = Math.round((rects.large.bottom - rects.middle.bottom) * 0.8f);

      Paddings.bottom = -h / 10;
      Paddings.vertical = -Paddings.bottom * 3;
      Paddings.diagonal = h;

      layoutPool();
      layoutLeftScore(h);
      layoutMiddleScore(h);
      layoutRightScore(h);
      layoutWatermark(left, width);
    }
  }

  @Override
  protected void onDraw(Canvas c)
  {
    super.onDraw(c);

    drawPool(c);
  }

  private void initializePool()
  {
    int spacing = radius = width / 10;
    int sqrt = (int)(radius/Math.sqrt(2));

    // large rectangle - reduce Lines.WIDTH / 2 from the bottom, otherwise the bottom line won't be drawn properly
    rects.large = new RectF(0, (height - width) / 2, width, (height - width) / 2 + width - Lines.WIDTH / 2);
    // middle rectangle
    rects.middle = new RectF(rects.large.left + spacing, rects.large.top, rects.large.right - spacing, rects.large.bottom - spacing);
    // small rectangle
    rects.small = new RectF(rects.middle.left + spacing, rects.middle.top, rects.middle.right - spacing, rects.middle.bottom - spacing);

    // vertical line
    lines.vertical = new Line((rects.large.left + rects.large.right)/2, rects.large.top, (rects.large.left + rects.large.right)/2, rects.large.centerY() - radius);
    // left diagonal line
    lines.left = new Line(rects.large.left, rects.large.bottom, rects.large.centerX() - sqrt, rects.large.centerY() + sqrt);
    // right diagonal line
    lines.right = new Line(rects.large.right, rects.large.bottom, rects.large.centerX() + sqrt, rects.large.centerY() + sqrt);
    // left whist line
    lines.leftWhist = new Line(rects.large.left, (rects.large.bottom + rects.large.top)/2, rects.middle.left, (rects.large.bottom + rects.large.top)/2);
    // middle whist line
    lines.middleWhist = new Line((rects.large.left + rects.large.right)/2, rects.large.bottom, (rects.large.left + rects.large.right)/2, rects.middle.bottom);
    // right whist line
    lines.rightWhist = new Line(rects.large.right, (rects.large.bottom + rects.large.top)/2, rects.middle.right, (rects.large.bottom + rects.large.top)/2);
  }

  private void drawPool(Canvas c)
  {
    // draw pool's rectangles
    c.drawRect(rects.small, Lines.paint);
    c.drawRect(rects.middle, Lines.paint);
    c.drawRect(rects.large, Lines.paint);

    // draw pool's lines
    lines.vertical.draw(c);
    lines.left.draw(c);
    lines.right.draw(c);

    // draw pool's circle
    c.drawCircle(rects.large.centerX(), rects.large.centerY(), radius, Lines.paint);

    // draw pool's whist lines
    lines.leftWhist.draw(c);
    lines.middleWhist.draw(c);
    lines.rightWhist.draw(c);
  }

  private void layoutPool()
  {
    //int size = Math.round((float)(2 * radius / Math.sqrt(2)));
    int size = radius * 2;

    int l = center.x - size / 2;
    int t = center.y - size / 2;

    pool.measure(MeasureSpec.EXACTLY | size, MeasureSpec.EXACTLY | size);
    pool.layout(l, t, l + size, t + size);
    pool.setTextSize(TypedValue.COMPLEX_UNIT_PX, pool.getMeasuredHeight() * (settings.isRostov() ? 0.3f : 0.6f));
    //pool.setTextScaleX(0.8f);
    //pool.setBackgroundColor(Color.RED);

    StringBuilder sb = new StringBuilder(8);

    if (settings.isRostov())
      sb.append(settings.poolSize / 2).append("/").append(settings.poolSize);
    else
      sb.append(settings.poolSize);

    pool.setText(sb.toString());
  }

  private void layoutLeftScore(int h)
  {
    Rect mountain   = new Rect();
    Rect pool       = new Rect();
    Rect whistLeft  = new Rect();
    Rect whistRight = new Rect();
    Rect balance    = new Rect();

    int t = Math.round(rects.small.bottom - Paddings.bottom - h);
    mountain.set(Paddings.vertical, t, Math.round(rects.small.right - Paddings.diagonal), t + h);

    t = Math.round(rects.middle.bottom - Paddings.bottom - h);
    pool.set(Paddings.vertical, t, Math.round(rects.middle.right - Paddings.diagonal ), t + h);

    t = Math.round(rects.large.bottom - Paddings.bottom - h);
    whistLeft.set(Paddings.vertical, t, Math.round(lines.middleWhist.start.x - Paddings.vertical), t + h);
    whistRight.set(Math.round(lines.middleWhist.start.x + Paddings.vertical), t, Math.round(rects.large.right - Paddings.diagonal), t + h);

    int l = center.x - 3 * radius;
    int bh = Math.round(Paddings.BalanceFactor.HEIGHT * h);
    t = GUI.center(bh, center.y + radius, mountain.top);

    balance.set(l, t, l + 4 * radius, t + bh);

    leftScore.layout(mountain, pool, whistLeft, whistRight, balance);
  }

  private void layoutMiddleScore(int h)
  {
    Rect mountain   = new Rect();
    Rect pool       = new Rect();
    Rect whistLeft  = new Rect();
    Rect whistRight = new Rect();
    Rect balance    = new Rect();

    int t = Math.round(rects.small.bottom - Paddings.bottom - h);
    mountain.set(Math.round(rects.small.left + Paddings.diagonal), t, Math.round(rects.small.right - Paddings.diagonal), t + h);

    t = Math.round(rects.middle.bottom - Paddings.bottom - h);
    pool.set(Math.round(rects.middle.left + Paddings.diagonal), t, Math.round(rects.middle.right - Paddings.diagonal), t + h);

    t = Math.round(rects.large.bottom - Paddings.bottom - h);
    whistLeft.set(Math.round(rects.large.left + Paddings.diagonal), t, Math.round(lines.middleWhist.start.x - Paddings.vertical), t + h);
    whistRight.set(Math.round(lines.middleWhist.start.x + Paddings.vertical), t, Math.round(rects.large.right - Paddings.diagonal), t + h);

    int l = Math.round(center.x - radius * Paddings.BalanceFactor.MIDDLE_WIDTH);
    int bh = Math.round(Paddings.BalanceFactor.HEIGHT * h);
    t = GUI.center(bh, center.y + radius, mountain.top);

    balance.set(l, t, Math.round(center.x + radius * Paddings.BalanceFactor.MIDDLE_WIDTH), t + bh);

    middleScore.layout(mountain, pool, whistLeft, whistRight, balance);
  }

  private void layoutRightScore(int h)
  {
    Rect mountain   = new Rect();
    Rect pool       = new Rect();
    Rect whistLeft  = new Rect();
    Rect whistRight = new Rect();
    Rect balance    = new Rect();

    int t = Math.round(rects.small.bottom - Paddings.bottom - h);
    mountain.set(Math.round(rects.small.left + Paddings.diagonal), t, Math.round(rects.large.right - Paddings.vertical), t + h);

    t = Math.round(rects.middle.bottom - Paddings.bottom - h);
    pool.set(Math.round(rects.middle.left + Paddings.diagonal), t, Math.round(rects.large.right - Paddings.vertical), t + h);

    t = Math.round(rects.large.bottom - Paddings.bottom - h);
    whistLeft.set(Paddings.diagonal, t, Math.round(lines.middleWhist.start.x - Paddings.vertical), t + h);
    whistRight.set(Math.round(lines.middleWhist.start.x + Paddings.vertical), t, Math.round(rects.large.right - Paddings.vertical), t + h);

    int l = center.x - radius;
    int bh = Math.round(Paddings.BalanceFactor.HEIGHT * h);
    t = GUI.center(bh, center.y + radius, mountain.top);

    balance.set(l, t, l + 4 * radius, t + bh);

    rightScore.layout(mountain, pool, whistLeft, whistRight, balance);
  }

  private void layoutWatermark(int l, int w)
  {
    int h = w / 3;
    int t = center.y - h / 2;

    watermark.measure(MeasureSpec.EXACTLY | w, MeasureSpec.EXACTLY | h);
    watermark.layout(l, t, l + w, t + h);

    GUI.FitText.byFactors(watermark, 0.95f, 0.7f);

    // make animation only if watermark shown
/*
    if (watermark.getVisibility() == VISIBLE)
    {
      RotateAnimation anim = new RotateAnimation(0, -45, Animation.ABSOLUTE, watermark.getWidth() / 2, Animation.ABSOLUTE, watermark.getHeight() / 2);

      anim.setFillAfter(true);
      anim.setDuration(0);

      watermark.startAnimation(anim);
    }
*/
  }

  private static final String DOT = ".";
  private static final String CLOSED = ">>";

  private static final class Paddings
  {
    // balance's text size factors
    private static final class BalanceFactor
    {
      private static final float MIDDLE_WIDTH = 1.6f;
      private static final float HEIGHT       = 1.2f;
    }

    // offsets
    static int bottom;
    static int vertical;
    static int diagonal;
  }

  private static class Rectangles
  {
    RectF small;
    RectF middle;
    RectF large;
  }

  private static class Line
  {
    PointF start;
    PointF end;

    Line(float startX, float startY, float endX, float endY)
    {
      start = new PointF(startX, startY);
      end = new PointF(endX, endY);
    }

    void draw(Canvas c)
    {
      c.drawLine(start.x, start.y, end.x, end.y, Lines.paint);
    }
  }

  private static class Lines
  {
    static final int COLOR = 0xFF000000;
    static final int WIDTH = 2;

    static final Paint paint = new Paint();

    Line vertical;
    Line left;
    Line right;

    Line leftWhist;
    Line middleWhist;
    Line rightWhist;

    static
    {
      paint.setStyle(Paint.Style.STROKE);
      paint.setColor(COLOR);
      paint.setTypeface(GUI.Font.BOLD);
      paint.setAntiAlias(true);
      paint.setStrokeWidth(WIDTH);
    }
  }

  private class PlayerScore
  {
    PlayerScore(Player p)
    {
      player = p;

      mountain = createTextView(GUI.Font.BOLD);
      pool = createTextView(GUI.Font.BOLD);
      whistNext = createTextView(GUI.Font.BOLD);
      whistPrevious = createTextView(GUI.Font.BOLD);

      balance = createTextView(GUI.Font.NORMAL);
      balance.setGravity(Gravity.CENTER);
    }

    TextView createTextView(Typeface tf)
    {
      ViewGroup parent = PoolView.this;

      TextView tv = new TextView(parent.getContext());

      tv.setSingleLine();
      tv.setTypeface(tf);
      tv.setTextColor(Color.BLACK);
      tv.setEllipsize(TextUtils.TruncateAt.START);

      parent.addView(tv);

      return tv;
    }

    void layout(Rect area, TextView tv)
    {
      // measure, layout and setTextSize
      tv.measure(MeasureSpec.EXACTLY | area.width(), MeasureSpec.EXACTLY | area.height());
      tv.layout(area.left, area.top, area.right, area.bottom);
      tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getMeasuredHeight() * 0.7f);
      //tv.setBackgroundColor(Color.YELLOW);
    }

    void layout(Rect m, Rect p, Rect wl, Rect wr, Rect b)
    {
      layout(m, mountain);
      layout(p, pool);
      layout(wl, whistNext);
      layout(wr, whistPrevious);
      layout(b, balance);
    }

    void rotate(int degrees, int centerX, int centerY)
    {
      rotate(mountain, degrees, centerX, centerY);
      rotate(pool, degrees, centerX, centerY);
      rotate(whistNext, degrees, centerX, centerY);
      rotate(whistPrevious, degrees, centerX, centerY);
      rotate(balance, degrees, centerX, centerY);
    }

    void rotate(final TextView tv, int degrees, int centerX, int centerY)
    {
      RotateAnimation anim = new RotateAnimation(0, degrees, Animation.ABSOLUTE, centerX - tv.getLeft(), Animation.ABSOLUTE, centerY - tv.getTop());

      anim.setFillAfter(true);
      anim.setDuration(0);
      anim.setAnimationListener(new Animation.AnimationListener()
      {
        public void onAnimationStart(Animation animation)
        {
        }

        public void onAnimationEnd(Animation animation)
        {
          performedAnimations++;

          if (performedAnimations == NUM_OF_ANIMATIONS)
          {
            leftScore.show();
            middleScore.show();
            rightScore.show();
          }
        }

        public void onAnimationRepeat(Animation animation)
        {
        }
      });

      tv.startAnimation(anim);
    }

    void show()
    {
      Score score = profile.game().getScore(player.id());
      Player previous = profile.game().previous(player);
      Player next = profile.game().next(player);
      boolean over = calculator.over(profile.game(), settings);

      // show scores
      show(mountain, score.mountain, false, over);
      show(pool, score.pool, calculator.closed(profile.game(), settings, player), false);
      show(whistPrevious, score.whists[previous.id()], false, over);
      show(whistNext, score.whists[next.id()], false, over);

      // show balance
      long b = score.balance2.current;
      long d = score.balance2.delta;

      if (over)
        balance.setText(Html.fromHtml(String.format(Format.end, b < 0 ? Colors.RED : Colors.GREEN, b > 0 ? "+" : "", b)));
      else
        balance.setText(Html.fromHtml(String.format(Format.intermediate, b < 0 ? Colors.RED : Colors.GREEN, b > 0 ? "+" : "", b, d < 0 ? Colors.RED : Colors.GREEN, d > 0 ? "▲" : (d < 0 ? "▼" : ""), Math.abs(d))));
    }

    final class Format
    {
      static final String intermediate = "<b><font color=\"%s\">%s%d</font></b><sub>&nbsp;<small><small><font color=\"%s\">%s<small>%d</small></font></small></small></sub>";
      static final String end = "<b><font color=\"%s\">%s%d</font></b>";
    }

    void show(TextView tv, Score.Values values, boolean closed, boolean over)
    {
      if (!settings.isRostov() && values.current == 0 && !closed && !over)
        return;

      SpannableStringBuilder ssb = new SpannableStringBuilder();

      for (Iterator<Long> i = values.history.iterator(); i.hasNext();)
      {
        ssb.append(String.valueOf(i.next()));

        if (i.hasNext())
          ssb.append(DOT);
      }

      // show history and current values
      if (values.current == 0)
      {
        if (closed)
          ssb.append(CLOSED);
        else
          // in Rostov mountain can/must reach 0 i.e. it should be drawn
          //if (settings.isRostov())
          // when mountain reaches 0 during the game it should be shown
          if (values.history.size() > 0)
          {
            ssb.append(DOT);
            ssb.append(values.changed ? GUI.getColoredText(Colors.CHANGED, "0") : "0");
          }
      }
      else
      {
        StringBuilder current = new StringBuilder(64);

        if (values.history.size() > 0)
          current.append(DOT);

        current.append(String.valueOf(values.current));

        if (closed)
          current.append(CLOSED);

        ssb.append(values.changed ? GUI.getColoredText(Colors.CHANGED, current.toString()) : current);
      }

      // append eventual if needed - relevant only for mountain and whists
      if (over)
      {
        long e = ((Score.Closable)values).eventual();

        if (values.current != e)
        {
          StringBuilder eventual = new StringBuilder(16);

          if (ssb.length() > 0)
            eventual.append(DOT);

          eventual.append(String.valueOf(e));

          ssb.append(GUI.getColoredText(Colors.EVENTUAL, eventual.toString()));
        }
      }

      tv.setText(ssb);
    }

    // player that owns this score
    Player player;

    // widgets
    TextView mountain;
    TextView pool;
    TextView whistNext;
    TextView whistPrevious;
    TextView balance;
  }

  private final static int NUM_OF_ANIMATIONS = 10;

  private static class Colors
  {
    private final static String RED      = "red";
    private final static String GREEN    = "#008800";
    private final static String CHANGED  = "#4169e1";
    private final static String EVENTUAL = "#FF8C00";
  }

  // profile
  private Profile profile;
  // settings object
  private Settings settings;
  // calculator object
  private Calculator calculator;

  // view's dimensions
  private int width;
  private int height;

  // view's center point
  private Point center = new Point();

  // pool's circle radius
  private int radius;

  // rectangles
  private Rectangles rects = new Rectangles();

  // lines
  private Lines lines = new Lines();

  // widgets
  private TextView pool;
  private TextView watermark;

  // scores of players
  private PlayerScore leftScore;
  private PlayerScore middleScore;
  private PlayerScore rightScore;

  // performed animations so far
  private int performedAnimations = 0;

  // needed to resolve 'onMeasure' issue (dialog wasn't centered and buttons were cut)
  private int size;
}
